﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HumanWorkflow.Interface;
using HumanWorkflow.SemanticModel;

namespace HumanWorkflow.Framework
{
    public class HumanWorkflowEngine : IHumanWorkflow
    {
        HumanWorkflowSemanticModel semanticModel;
        Step currentStep;

        public HumanWorkflowEngine(HumanWorkflowSemanticModel semanticModel)
        {
            this.semanticModel = semanticModel;

            var initialSteps = semanticModel.Steps.Where<Step>(s => s is InitialStep);
            
            if (initialSteps.Count<Step>() > 1)
            {
                throw new ArgumentException("Semantic model contains more than one initial step.");
            }
            else if (initialSteps.Count<Step>() == 0)
            {
                throw new ArgumentException("Semantic does not contain an initial step.");            
            }
            else
            {
                currentStep = semanticModel.Steps.Where<Step>(s => s is InitialStep).First<Step>();
            }
        }

        public string CurrentStep
        {
            get { return currentStep.Name; }
        }

        public string CurrentRole
        {
            get 
            {
                if (currentStep is ManualStep)
                {
                    return ((ManualStep)currentStep).Role;
                }
                else
                {
                    throw new Exception("Role is only applicable to manual steps.");
                }
                
            }
        }

        public List<string> GetValidTransitions()
        {
            List<string> validTransitionNames = new List<string>();
            foreach (Transition tr in currentStep.OutTransitions)
            {
                validTransitionNames.Add(tr.Name);
            }

            return validTransitionNames;
        }


        public void RaiseTransition(string transitionName)
        {
            if (!GetValidTransitions().Contains(transitionName))
            {
                throw new ArgumentException("The provided transition is not valid for the current step.");
            }

            Step newStep = semanticModel.FindTransitionByName(transitionName).ToStep;
            string nextAutomaticTransitionName = null;

            OnBeforeStep(new TransitionEventArgs(transitionName, CurrentStep));

            currentStep = newStep;

            if (newStep is DecisionStep || newStep is AutomaticStep)
            {
                nextAutomaticTransitionName = ExecuteAutomaticStep(currentStep);    
            }

            OnAfterStep(new TransitionEventArgs(transitionName, CurrentStep));

            // Execute automatic transition in case of Decision or AutomaticStep
            if (nextAutomaticTransitionName != null)
            {
                RaiseTransition(nextAutomaticTransitionName);
            }
        }

        /// <summary>
        /// Must be implemented if there is an automatically executed step in the workflow.
        /// </summary>
        /// <param name="executedStep">Executed step.</param>
        /// <returns>The transition" name to be made after the execution of the automatic step.</returns>
        public virtual string ExecuteAutomaticStep(Step executedStep)
        {
            throw new NotImplementedException();
        }

        public event EventHandler BeforeStep;

        void OnBeforeStep(EventArgs e)
        {
            if (BeforeStep != null)
                BeforeStep(this, e);
        }

        public event EventHandler AfterStep;

        void OnAfterStep(EventArgs e)
        {
            if (AfterStep != null)
                AfterStep(this, e);            
        }
    }
}
